import contextlib
import json

from dojo.models import Finding


class HarborVulnerabilityParser:

    """Read JSON data from Harbor compatible format and import it to DefectDojo"""

    def get_scan_types(self):
        return ["Harbor Vulnerability Scan"]

    def get_label_for_scan_types(self, scan_type):
        return scan_type  # no custom label for now

    def get_description_for_scan_types(self, scan_type):
        return "Import vulnerabilities from Harbor API."

    def get_findings(self, filename, test):
        tree = filename.read()
        try:
            data = json.loads(str(tree, "utf-8"))
        except Exception:
            data = json.loads(tree)

        # When doing dictionary, we can detect duplications
        dupes = {}

        # json output of https://pypi.org/project/harborapi/
        with contextlib.suppress(KeyError):
            vulnerability = data["vulnerabilities"]
        # To be compatible with update in version
        with contextlib.suppress(KeyError, StopIteration, TypeError):
            vulnerability = data[next(iter(data.keys()))]["vulnerabilities"]

        # Early exit if empty
        if "vulnerability" not in locals() or vulnerability is None:
            return []

        for item in vulnerability:
            item_id = item.get("id")
            package_name = item.get("package")
            package_version = item.get("version")
            description = item.get("description", "No description found")
            severity = item.get("severity")
            fix_version = item.get("fix_version")
            links = item.get("links")
            cwe_ids = item.get("cwe_ids")
            fix_available = True
            if not item.get("fix_version"):
                fix_available = False

            title = f"{item_id} - {package_name} ({package_version})"
            severity = transpose_severity(severity)

            mitigation = f"Upgrade {package_name} to version {fix_version}" if fix_version else None

            if links:
                references = ""
                for link in links:
                    references += f"{link}\n"
            else:
                references = None

            cwe = cwe_ids[0].strip("CWE-") if cwe_ids and cwe_ids[0] else None

            vulnerability_id = item_id if item_id and item_id.startswith("CVE") else None

            dupe_key = title

            if dupe_key in dupes:
                find = dupes[dupe_key]
            else:
                dupes[dupe_key] = True

                find = Finding(
                    title=title,
                    test=test,
                    description=description,
                    severity=severity,
                    mitigation=mitigation,
                    references=references,
                    static_finding=True,
                    component_name=package_name,
                    component_version=package_version,
                    cwe=cwe,
                    fix_available=fix_available,
                )
                if vulnerability_id:
                    find.unsaved_vulnerability_ids = [vulnerability_id]
                dupes[dupe_key] = find

        return list(dupes.values())


def transpose_severity(severity):
    if severity in Finding.SEVERITIES:
        return severity
    return "Info"
